﻿/*
 * Liste avec fleches de défilement droite - gauche
 * 
 * targets:Array : les DisplayObjects à afficher, nombre indéfini
 * _places:Array : les différentes props { x, y, scaleX, scaleY, rotation, alpha, width, height } affectés aux targets pendant la navigation
 * current : affichage courant, index de la target située à la place[ 0 ]
 * */
package fab.controls.liste
{
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import caurina.transitions.properties.FilterShortcuts;
	import caurina.transitions.Tweener;
	import flash.events.TimerEvent;
	import flash.filters.DropShadowFilter;
	import flash.utils.Timer;
	import flash.filters.BlurFilter;
	import flash.utils.ByteArray;
	
	public class Liste extends Sprite
	{
		static public var CHANGE:String="CHANGE";
		
		/*
		 * index de la place qui doit se trouver devant
		 * */		
		public var frontPlace:int = 0;
		/*
		 * avant le 0, le dernier ?
		 * */			
		public var looped:Boolean = false;
		
		private var _places:Array;
		private var provider_places:Array;
		private var targets:Array;
		private var current:int;
		private var updatenext:int;
		private var fleche_droite:DisplayObject;
		private var fleche_gauche:DisplayObject;
		private var delay:int;
		private var timer:Timer;
		private var sens:Boolean=true;
		private var next:int;
		private var content:Sprite;
		private var nextfunc:Function;
		

		public function Liste()
		{
			//FilterShortcuts.init();
			addChild( content = new Sprite() );
		}
		/*
		 * info object
		 * */
		public function get info():Object
		{
			return { current:current, frontPlace:frontPlace, looped:looped, targetsLength:targets.length, placesLength:_places.length};
		}
		/*
		 * toString
		 * */
		override public function toString():String
		{
			return "Liste current:" + current + ", frontPlace:" + frontPlace + ", looped:" + looped + ", targetsLength:" + targets.length + ", placesLength:" + _places.length;
		}			
		/*
		 * lance un timer pour la rotation des targets, delay (ms) doit être supérieur au time (s) des _places
		 * */
		public function autorotate( _delay:uint, _sens:Boolean=true ):void
		{
			delay = _delay;
			sens = _sens;
			updatenext = undefined;
			timer = new Timer( delay );
			timer.addEventListener( TimerEvent.TIMER, _next_time );
			timer.start();
		}
		/*
		 * place 2 DisplayObject pour la navigation incrémentale, le 2eme peut être tourné
		 * */
		public function fleches( sprite1:*, sprite2:*, xleft:int=0, yleft:int=0, xright:int=0, yright:int=0, rotation:Boolean=true ):void
		{
			fleche_gauche = sprite1;
			addChild( fleche_gauche );
			fleche_gauche.x = xleft;
			fleche_gauche.y = yleft;
			fleche_gauche.addEventListener(MouseEvent.CLICK, _fleche_gauche );	
			if ( rotation ) fleche_gauche.rotation = 180;
			
			fleche_droite = sprite2;
			addChild( fleche_droite );
			fleche_droite.x = xright;
			fleche_droite.y = yright;
			fleche_droite.addEventListener(MouseEvent.CLICK, _fleche_droite );
		}
		/*
		 * renseigne le tableau places
		 * */			
		public function set places( p:Array ):void
		{
			_places = clone( p );
			provider_places = clone( p );
			for each ( var place:Object in _places )
			{
				if ( place.time == null ) place.time = .5;
				if ( place.transition == null ) place.transition = "easeOutQuint";
				//if ( place.shadow != null ) place.shadow = null;
			}			
		}
		public function get places():Array
		{
			return provider_places;
		}
		/*
		 * renseigne le tableau targets, et affiche les targets aux places, avec en option viderListe et le target courant à la place 0
		 * */		
		public function update( _targets:Array, viderListe:Boolean = true, _current:int = 0):void
		{
			if ( viderListe )
			{
				clear();
				targets = _targets;
				current = _current;
				if ( targets != null && _places != null ) placeTargets();
			}
			else {
				targets = targets.concat( _targets );
			}
			update_fleches();
		}
		/*
		 * lance la rotation des targets jusqu'à placer targets[ _next ] à la place 0
		 * */			
		public function goto( _next:int = 0 ):void
		{
			if ( current == _next ) return;
			updatenext = _next;
			var d:int = difference_min( updatenext, current, targets.length );
			sens = d >= 0;
			incremente( sens?1: -1, d );
		}
		/*
		 *  lance la rotation des targets jusqu'à placer target à la place frontPlace
		 * */		
		public function gotoTarget( target:DisplayObject ):void
		{
			var index:int = getIndexByTarget( target );
			goto( index );
		}		
		/*
		 * vide le sprite content
		 * */			
		public function clear():void
		{
			for each ( var target:DisplayObject in targets ) if ( target && content.contains( target ) ) content.removeChild( target );
		}
		/*
		 * affiche les targets aux places, avec le target courant à la place 0
		 * */			
		public function placeTargets():void
		{
			for ( var i:int = 0; i < _places.length; i++ )
			{
				if ( !looped && ( current + i - frontPlace ) < 0 ) continue;
				if ( !looped && ( current + i - frontPlace ) > targets.length - 1 ) continue;
				var target:DisplayObject = targets[ ( current - frontPlace + i + targets.length ) % targets.length ];
				placeTarget( content.addChildAt( target, i == frontPlace?Math.max(0, content.numChildren - 1):0 ) , _places[ i ] );
			}
		}
		/*
		 * lance une étape de rotation des targets vers la droite ou la gauche, avec en option la relance en fin de transition
		 * transition_step : le nombre de transitions restantes, permet de régler la vitesse de transition pour accélerer selon la distance à la cible
		 * */			
		public function incremente( increment:int = 1, transition_step:int = 0 ):void
		{
			if ( targets.length == 0 ) return;
			if ( _places.length == 0 ) return clear();
			for each ( var target:DisplayObject in targets ) if ( Tweener.isTweening( target ) ) return;

			next = ( current + increment + targets.length ) % targets.length;
			//next = ( current + increment );
			
			if ( transition_step != 0 )
			{
				var maxtime:Number = 0;
				for each ( var place:Object in _places )
				{
					place.transition = "linear";
					place.time = provider_places[ frontPlace ].time / Math.abs( transition_step );// fixe le temps d'une transition//_places[ frontPlace ].time
					maxtime = Math.max( maxtime, place.time );
				}
			}
			targets.forEach( moveTarget );
			if ( transition_step != 0 ) Tweener.addTween( { }, { time:maxtime, onComplete:_next_step } );

			current = next;
			update_fleches();
			dispatchEvent( new Event( CHANGE ) );
		}
		public function getTargetAt(index:int):DisplayObject
		{
			return targets[ current + index - frontPlace ];
		}
		public function getIndexByTarget(placetarget:DisplayObject):int
		{
			for ( var index:int = 0; index < targets.length; index++ ) if ( targets[ index ] == placetarget ) return index;
			return 0;
		}		
		public function getPlaceByTarget(placetarget:DisplayObject):int
		{
			for ( var index:int = 0; index < targets.length; index++ ) if ( targets[ index ] == placetarget ) break;
			return index - current + frontPlace;
		}
		private function update_fleches():void 
		{
			var targetslist_trop_courte:Boolean = !looped?(targets.length <= _places.length - 2):false;
			if ( fleche_droite ) fleche_droite.visible = !targetslist_trop_courte && ( !looped?current < targets.length - 1:true );
			if ( fleche_gauche ) fleche_gauche.visible = !targetslist_trop_courte && ( !looped?current > 0 :true);
		}
		private function _fleche_gauche(event:MouseEvent):void
		{
			if ( timer && timer.running ) sens = false;
			else incremente( -1 );
		}
		private function _fleche_droite(event:MouseEvent):void
		{
			if ( timer && timer.running ) sens = true;
			else incremente( 1 );
		}

		private function _next_time(e:TimerEvent):void 
		{
			incremente( sens?1: -1 );
		}
		private function _next_step():void 
		{
			if ( difference_min( updatenext, current, targets.length ) == 0 )
			{
				_places = clone( provider_places );
				updatenext = undefined;
				timer = null;
			}
			else {
				incremente( sens?1:-1, difference_min( updatenext, current, targets.length ) );
			}
		}

		private function distance_min( _next:int, _current:int, _length:int ):int
		{
			return Math.abs( difference_min( _next, _current, _length ) );
		}		
		private function difference_min( _next:int, _current:int, _length:int ):int
		{
			var d1:int = _next - _current;
			if ( !looped ) return d1;
			var d2:int = Math.min( _next, _current ) + _length - Math.max( _next, _current );
			if ( _next > _current ) d2 = - d2;
			return Math.abs( d1 ) >= Math.abs( d2 )?d2:d1;
		}
        private function moveTarget( target:DisplayObject, index:int, arr:Array ):void
		{
			var placeindex:int = difference_min( index, next, targets.length ) + frontPlace;
			if ( ( placeindex >= 0 && placeindex < _places.length ) )
			{
				if ( content.contains( target ) ) Tweener.addTween( target, _places[ placeindex ] );
				else placeTarget( content.addChild( target ) , _places[ placeindex ] );
				content.setChildIndex( target, placeindex == frontPlace?content.numChildren - 1:0 );	
			}
			else if ( content.contains( target ) ) content.removeChild( target );
        }
		private function placeTarget( target:DisplayObject, place:Object ):void
		{
			if ( target == null ) return;
			if ( place == null ) return;

			if ( !isNaN( place.x ) ) target.x = place.x;
			if ( !isNaN( place.y ) ) target.y = place.y;
			if ( !isNaN( place.rotation ) ) target.rotation = place.rotation;
			if ( !isNaN( place.scaleX ) ) target.scaleX = place.scaleX;
			if ( !isNaN( place.scaleY ) ) target.scaleY = place.scaleY;
			if ( !isNaN( place.alpha ) ) target.alpha = place.alpha;
			if ( !isNaN( place.width ) ) target.width = place.width;
			if ( !isNaN( place.height ) ) target.height = place.height;
			
			if ( place._Blur_blurX != null || place._Blur_blurY != null )
			{
				var dp:BlurFilter = new BlurFilter( place._Blur_blurX?place._Blur_blurX:0, place._Blur_blurY?place._Blur_blurY:0 );
				target.filters = [ dp ];
			}
		}
		private function clone(source:Object):*
		{
			var myBA:ByteArray = new ByteArray();
			myBA.writeObject(source);
			myBA.position = 0;
			return(myBA.readObject());
		}		
	}
}